package com.bss.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.andy.basics.Dao;
import com.bss.dao.ActivityDao;
import com.bss.dao.AdmissionDao;
import com.bss.dao.AttendentDao;
import com.bss.dao.BedBookingDao;
import com.bss.dao.BuildingDao;
import com.bss.dao.CurrentAttendentDao;
import com.bss.dao.MaterialSetDao;
import com.bss.dao.MaterialSetMappingDao;
import com.bss.dao.PassportDao;
import com.bss.dao.PatientDao;
import com.bss.dao.PaymentDao;
import com.bss.dao.PaymentTypeDao;
import com.bss.dao.RoomBookingDao;
import com.bss.model.Admission;
import com.bss.model.Attendent;
import com.bss.model.Bed;
import com.bss.model.BedBooking;
import com.bss.model.Building;
import com.bss.model.CurrentAttendent;
import com.bss.model.MaterialAllocation;
import com.bss.model.MaterialSet;
import com.bss.model.MaterialSetMapping;
import com.bss.model.Patient;
import com.bss.model.Payment;
import com.bss.model.Receipt;
import com.bss.model.Room;
import com.bss.model.RoomBooking;

@Service
@Scope("session")
public class AdmissionService extends com.andy.basics.Service<Admission> {

	private static final Logger LOGGER = Logger.getLogger(AdmissionService.class.getName());

	@Autowired
	private AdmissionDao admissionDao;

	@Autowired
	private ActivityDao activityDao;

	@Autowired
	private PatientDao patientDao;

	@Autowired
	private BuildingDao buildingDao;

	@Autowired
	private PassportDao passportDao;

	@Autowired
	private GenericService genericService;

	@Autowired
	private CurrentAttendentDao currentAttendentDao;

	private DataModel patientsDatamodel;

	private DataModel currentAttendentsData;

	@Autowired
	private RoomBookingDao roomBookingDao;

	@Autowired
	private BedBookingDao bedBookingDao;

	@Autowired
	private AttendentDao attendentDao;

	@Autowired
	private MaterialSetDao materialSetDao;

	@Autowired
	private MaterialSetMappingDao materialSetMappingDao;

	private List<MaterialSet> materialSetList;

	private List<MaterialAllocation> materialAllocations;

	@Autowired
	private AttendentService attendentService;

	@Autowired
	private RoomOccupancyService roomOccupancyService;

	private List<CurrentAttendent> currentAttendents;

	private List<Attendent> attendents;

	private DataModel attendentsDataModel;

	private DataModel exitedAdmissions;

	private Admission exitableAdmission;

	private int utensilRefund;

	private int securityRefund;

	private List<Integer> rooms;

	private List<Integer> beds;

	@Autowired
	private PaymentTypeDao paymentTypeDao;

	@Autowired
	private PaymentDao paymentDao;

	private int securityDeposit;

	@Autowired
	private DailyDepositAndRefundService dailyDepositAndRefundService;

	private int utensilDeposit = 0;

	public List<CurrentAttendent> getCurrentAttendents() {
		return this.currentAttendents;
	}

	public void setMaterialAllocations(final List<MaterialAllocation> materialAllocations) {
		this.materialAllocations = materialAllocations;
	}

	public void setCurrentAttendents(final List<CurrentAttendent> currentAttendents) {
		this.currentAttendents = currentAttendents;
	}

	public void setCurrentAttendentsData(final DataModel currentAttendentsData) {
		this.currentAttendentsData = currentAttendentsData;
	}

	public DataModel getData() {
		if (this.currentAttendentsData == null) {
			this.currentAttendents = new ArrayList<CurrentAttendent>();
			this.currentAttendentsData = new ListDataModel(this.currentAttendents);
		}
		return this.currentAttendentsData;
	}

	public void add(final CurrentAttendent attendent) {
		this.currentAttendents.add(attendent);
		this.currentAttendentsData = new ListDataModel(this.currentAttendents);
	}

	@Override
	@Transactional
	public void save(final Admission admission) {

		if ("Passport".equalsIgnoreCase(admission.getPatient().getIdCardType().getName())) {
			if (admission.getPatient().getPassport().getId() == 0) {
				this.passportDao.persist(admission.getPatient().getPassport());
				// admission.getPatient().getPassport().setPerson(admission.getPatient());
			} else {
				this.passportDao.update(admission.getPatient().getPassport());
			}
		} else {
			admission.getPatient().setPassport(null);
		}

		if (admission.getPatient().getId() != 0) {
			this.patientDao.update(admission.getPatient());
		} else {
			this.patientDao.persist(admission.getPatient());
		}

		admission.setActive(true);
		this.getDao().persist(admission);

		if ((this.utensilDeposit != 0) && (this.getModel().getMaterialSet() != null)) {
			this.utensilDeposit(admission, this.utensilDeposit, null);
		}
		if (this.securityDeposit != 0) {
			this.securityDeposit(admission, this.securityDeposit, null);
		}

		for (CurrentAttendent ca : admission.getCurrentAttendents()) {
			if (ca.getAttendent().getId() == 0) {
				if ("Passport".equalsIgnoreCase(ca.getAttendent().getIdCardType().getName())) {
					if (ca.getAttendent().getPassport().getId() == 0) {
						this.passportDao.persist(ca.getAttendent().getPassport());
						// ca.getAttendent().getPassport().setPerson(ca.getAttendent());
					} else {
						this.passportDao.update(ca.getAttendent().getPassport());
					}
				} else {
					ca.getAttendent().setPassport(null);
				}
				this.attendentDao.persist(ca.getAttendent());
			} else {
				if ("Passport".equalsIgnoreCase(ca.getAttendent().getIdCardType().getName())) {
					if (ca.getAttendent().getPassport().getId() == 0) {
						this.passportDao.persist(ca.getAttendent().getPassport());
						// ca.getAttendent().getPassport().setPerson(ca.getAttendent());
					} else {
						this.passportDao.update(ca.getAttendent().getPassport());
					}
				} else {
					ca.getAttendent().setPassport(null);
				}
				this.attendentDao.update(ca.getAttendent());
			}
			this.currentAttendentDao.persist(ca);
		}

		this.reload();
		this.dailyDepositAndRefundService.reload();
		this.genericService.reloadCheckins();
	}

	protected Payment createSecurityDeposit(final Integer securityDepositRefundAmount, final Receipt receipt) {
		Payment payment = new Payment();
		payment.setAmount(securityDepositRefundAmount);
		payment.setPaymentType(this.paymentTypeDao.find("name='Security Deposit'").get(0));
		payment.setReceipt(receipt);
		return payment;
	}

	public void securityDeposit(final Admission admission, final Integer securityDepositAmount, final Receipt receipt) {
		Payment payment = this.createSecurityDeposit(securityDepositAmount, receipt);
		payment.setPayable(admission);
		this.paymentDao.persist(payment);
	}

	protected Payment createUtensilDeposit(final Integer utensilDepositAmount, final Receipt receipt) {
		Payment payment = new Payment();
		payment.setAmount(utensilDepositAmount);
		payment.setPaymentType(this.paymentTypeDao.find("name='Utensil Deposit'").get(0));
		payment.setReceipt(receipt);
		return payment;
	}

	public void utensilDeposit(final Admission admission, final Integer utensilDepositAmount, final Receipt receipt) {
		Payment payment = this.createUtensilDeposit(utensilDepositAmount, receipt);
		payment.setPayable(admission);
		this.paymentDao.persist(payment);
	}

	@Override
	public Dao<Admission> getDao() {
		return this.admissionDao;
	}

	public void deleteAttendent() {

		int i = this.currentAttendentsData.getRowIndex();
		CurrentAttendent ca = this.currentAttendents.get(i);

		if (this.attendents != null) {
			for (Attendent a : this.attendents) {
				if (a.getId().equals(ca.getAttendent().getId())) {
					a.setSelected(false);
				}
			}
		}

		this.currentAttendents.remove(i);
		this.currentAttendentsData = new ListDataModel(this.currentAttendents);
	}

	public void cancel() {
		this.reload();
	}

	public List<Building> getBuildings() {
		List<Building> buildings = this.roomOccupancyService.findBuildingsWithOccupancy();
		this.prepareSelected(buildings);
		return buildings;
	}

	protected void prepareSelected(List<Building> buildings) {
		for (Building building : buildings) {
			for (Room room : building.getRooms()) {
				if (!room.isDormitory()) {
					room.setSelected((this.rooms != null) && this.rooms.contains(room.getId()));
				} else {
					for (Bed bed : room.getBeds()) {
						bed.setSelected((this.beds != null) && this.beds.contains(bed.getId()));
					}
				}
			}
		}
	}

	public BuildingDao getBuildingDao() {
		return this.buildingDao;
	}

	public void setMaterialSetList(final List<MaterialSet> materialSetList) {
		this.materialSetList = materialSetList;
	}

	public List<MaterialSet> getMaterialSetList() {
		if (this.materialSetList == null) {
			this.materialSetList = this.materialSetDao.find("editable=0");
		}
		return this.materialSetList;
	}

	public List<MaterialAllocation> getMaterialAllocations() {

		if (this.materialAllocations != null) {
			return this.materialAllocations;
		}

		List<MaterialAllocation> materialAllocations = new ArrayList<MaterialAllocation>();

		for (MaterialSetMapping materialSetMapping : this.materialSetMappingDao.findEditableMaterialSetMappings()) {
			MaterialAllocation materialAllocation = new MaterialAllocation();
			materialAllocation.setMaterialType(materialSetMapping.getMaterialType());
			materialAllocation.setInstances(materialSetMapping.getInstances());
			materialAllocations.add(materialAllocation);
		}

		this.materialAllocations = materialAllocations;
		return materialAllocations;
	}

	@Override
	public void reload() {
		this.exitableAdmission = null;
		this.exitedAdmissions = null;
		this.currentAttendents = new ArrayList<CurrentAttendent>();
		this.attendents = null;
		this.exitedAdmissions = null;
		this.materialSetList = null;
		this.attendentsDataModel = null;
		this.utensilDeposit = 0;
		this.securityDeposit = 0;
		this.utensilRefund = 0;
		this.securityRefund = 0;
		this.rooms = null;
		this.beds = null;
	}

	protected List<Admission> fetchExitedAdmissions(final Date exitDate) {
		List<Admission> admissions = this.admissionDao.findExitsAfter(exitDate);
		return admissions;
	}

	protected List<Admission> initializeAdmissions(final Date exitDate, final List<Admission> admissions) {
		for (Admission admission : admissions) {

			this.populateAdmission(admission);
			admission.getCurrentAttendents().size();
			admission.getPayments().size();

			int roomRent = 0;
			for (RoomBooking roomBooking : admission.getRoomBookings()) {
				int days = this.getUtils().findDifferenceInDays(roomBooking.getCheckInDate(), exitDate);
				roomRent = roomRent + days * roomBooking.getRate();
			}
			int bedRent = 0;
			for (BedBooking bedBooking : admission.getBedBookings()) {
				int days = this.getUtils().findDifferenceInDays(bedBooking.getCheckInDate(), exitDate);
				bedRent = bedRent + days * bedBooking.getRate();
			}
		}
		return admissions;
	}

	@Transactional
	public void startCheckoutNew(final Date exitDate) {
		Admission admission = this.exitableAdmission;
		admission = this.admissionDao.findById(admission.getId());

		admission.setExitInited(true);

		for (CurrentAttendent currentAttendent : admission.getCurrentAttendents()) {
			currentAttendent.setDateOfLeave(exitDate);
		}
		for (MaterialAllocation materialAllocation : admission.getMaterialAllocations()) {
			materialAllocation.setDateReturned(exitDate);
		}
		for (RoomBooking roomBooking : admission.getRoomBookings()) {
			roomBooking.setCheckOutDate(exitDate);
		}
		for (BedBooking bedBooking : admission.getBedBookings()) {
			bedBooking.setCheckOutDate(exitDate);
		}

		admission.setDateOfExit(exitDate);
		admission.setActive(false);

		this.admissionDao.update(admission);

		if (this.utensilRefund != 0) {
			this.utensilDepositRefund(admission, this.utensilRefund, null);
		}
		if (this.securityRefund != 0) {
			this.securityDepositRefund(admission, this.securityRefund, null);
		}

		// for (RoomBooking roomBooking : admission.getRoomBookings()) {
		// this.roomOccupancyService.markRoomAllocated(roomBooking.getRoom().getId(),
		// false);
		// }
		// for (BedBooking bedBooking : admission.getBedBookings()) {
		// this.roomOccupancyService.markBedAllocated(bedBooking.getBed().getId(),
		// false);
		// }

		// this.roomOccupancyService.populateOccupancy(this.roomOccupancyService.getBuildings());

		this.reload();
		this.dailyDepositAndRefundService.reload();
		this.genericService.reloadCheckOuts();

	}

	public void utensilDepositRefund(final Admission admission, final Integer utensilDepositAmount, final Receipt receipt) {
		Payment payment = this.createUtensilDeposit(utensilDepositAmount, receipt);
		payment.setRefund(true);
		payment.setPayable(admission);
		this.paymentDao.persist(payment);
	}

	public void securityDepositRefund(final Admission admission, final Integer securityDepositRefundAmount, final Receipt receipt) {
		Payment payment = this.createSecurityDeposit(securityDepositRefundAmount, receipt);
		payment.setRefund(true);
		payment.setPayable(admission);
		this.paymentDao.persist(payment);
	}

	public DataModel getAttendentsDataModel() {
		return this.attendentsDataModel;
	}

	public List<Attendent> getAttendents() {
		return this.attendents;
	}

	public void select(final Patient patient) {
		this.attendents = this.attendentService.findAllAttendents(patient);
		this.attendentsDataModel = new ListDataModel(this.attendents);
	}

	public void selectAttendents() {
		for (Attendent a : this.attendents) {
			System.out.println(a.getName() + ": " + a.isSelected());
		}
	}

	public DataModel getPatients() {
		return this.patientsDatamodel;
	}

	@Transactional
	public DataModel getPatients(final String patientName) {
		List<Patient> patients = this.patientDao.findPatientsNotCheckedInCurrently(patientName.toString());
		this.patientsDatamodel = new ListDataModel(patients);
		return this.patientsDatamodel;
	}

	@Transactional
	public DataModel getExitedAdmissions() {
		if (this.exitedAdmissions == null) {
			Date date = this.activityDao.findLastDepositAndRefundReportActivityDate();
			this.exitedAdmissions = new ListDataModel(this.fetchExitedAdmissions(date == null ? new Date() : date));
		}
		return this.exitedAdmissions;
	}

	@Transactional
	public void populateAdmission(Admission admission) {
		admission.setRoomBookings(this.roomBookingDao.find("admission.id = " + admission.getId()
				+ " and checkinDate is not null and checkOutDate is null"));
		admission.setBedBookings(this.bedBookingDao.find("admission.id = " + admission.getId()
				+ " and checkinDate is not null and checkOutDate is null"));

	}

	public void selectDate(Date exitDate) {
		List<Admission> admissions = this.admissionDao.findExitsForDate(exitDate);
		this.exitedAdmissions = new ListDataModel(admissions);
	}

	public void clearRoomAllocation() {
		// this.roomOccupancyService.populateOccupancy(this.getBuildings());
	}

	public void setExitableAdmission(Admission admission) {
		this.exitableAdmission = admission;
	}

	public Admission getExitableAdmission() {
		return this.exitableAdmission;
	}

	public int getSecurityDeposit() {
		return this.securityDeposit;
	}

	public void setSecurityDeposit(int deposit) {
		this.securityDeposit = deposit;
	}

	public int getUtensilDeposit() {
		return this.utensilDeposit;
	}

	public void setUtensilDeposit(int deposit) {
		this.utensilDeposit = deposit;
	}

	public int getUtensilRefund() {
		return this.utensilRefund;
	}

	public void setUtensilRefund(int utensilRefund) {
		this.utensilRefund = utensilRefund;
	}

	public int getSecurityRefund() {
		return this.securityRefund;
	}

	public void setSecurityRefund(int securityRefund) {
		this.securityRefund = securityRefund;
	}

	public void setDeposits(Admission exitableAdmission) {
		this.securityDeposit = 0;
		this.utensilDeposit = 0;
		this.securityRefund = 0;
		this.utensilRefund = 0;

		for (Payment payment : exitableAdmission.getPayments()) {
			if (payment.getPaymentType().getName().equals("Security Deposit")) {
				if (!payment.isRefund()) {
					this.securityDeposit += payment.getAmount();
				} else {
					this.securityDeposit -= payment.getAmount();
				}
			}
			if (payment.getPaymentType().getName().equals("Utensil Deposit")) {
				if (!payment.isRefund()) {
					this.utensilDeposit += payment.getAmount();
				} else {
					this.utensilDeposit -= payment.getAmount();
				}
			}
		}
		this.securityRefund = (this.securityDeposit < 0 ? 0 : this.securityDeposit);
		this.utensilRefund = (this.utensilDeposit < 0 ? 0 : this.utensilDeposit);
	}

	public void setRooms(List<Integer> rooms) {
		this.rooms = rooms;
	}

	public List<Integer> getRooms() {
		return this.rooms;
	}

	public void setBeds(List<Integer> beds) {
		this.beds = beds;
	}

	public List<Integer> getBeds() {
		return this.beds;
	}

}
